package com.labot.webapp.action;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts2.ServletActionContext;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.mail.MailException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;

import com.labot.Constants;
import com.labot.dao.SearchException;
import com.labot.dto.UserDto;
import com.labot.dto.mapper.UserMapper;
import com.labot.model.Area;
import com.labot.model.Centro;
import com.labot.model.User;
import com.labot.service.UserExistsException;
import com.labot.types.AuditType;
import com.labot.types.LoginType;
import com.labot.util.Md5Utils;
import com.labot.webapp.action.messages.TextMessagesType;
import com.labot.webapp.util.RequestUtil;
import com.opensymphony.xwork2.Preparable;

/**
 * Action for facilitating User Management feature.
 */
public class UserAction extends BaseAction implements Preparable {
    private static final long serialVersionUID = 6776558938712115191L;
    
    private static final String SUCCESS_LDAP = "success_ldap";
    
    private List<UserDto> users;
    private UserDto user;
    private String id;
    private String query;
    private Boolean encryptPass;
    private Long idarea;
    private Long idcentro;
    
    private String username;
    private InputStream imageView;
    
    Map<Long,String> comboareas = new HashMap<Long,String>();
    Map<Long,String> combocentros = new HashMap<Long,String>();
    
    private Long currentcentro;
    private Long currentarea;
    
    /**
     * Grab the entity from the database before populating with request parameters
     */
    public void prepare() {
        // prevent failures on new
        if (getRequest().getMethod().equalsIgnoreCase("post") && (!"".equals(getRequest().getParameter("user.id")))) {
            user = UserMapper.mapModelToDto(userManager.getUser(getRequest().getParameter("user.id")));
        }
    }

    /**
     * Holder for users to display on list screen
     *
     * @return list of users
     */
    public List<UserDto> getUsers() {
        return users;
    }

    public void setId(String id) {
        this.id = id;
    }

    public UserDto getUser() {
        return user;
    }

    public void setUser(UserDto user) {
        this.user = user;
    }

    public void setQ(String q) {
        this.query = q;
    }

    /**
     * Delete the user passed in.
     *
     * @return success
     */
    public String delete() {
        userManager.removeUser(user.getId().toString());
        List<Object> args = new ArrayList<Object>();
        args.add(user.getFullName());
        saveMessage(getText(TextMessagesType.USERACTION_DELETE.getKey(), args));

        return SUCCESS;
    }

    /**
     * Grab the user from the database based on the "id" passed in.
     *
     * @return success if user found
     * @throws IOException can happen when sending a "forbidden" from response.sendError()
     */
    public String edit() throws IOException {
        HttpServletRequest request = getRequest();
        boolean editProfile = request.getRequestURI().contains("editProfile");

        // if URL is "editProfile" - make sure it's the current user
        if (editProfile && ((request.getParameter("id") != null) || (request.getParameter("from") != null))) {
            ServletActionContext.getResponse().sendError(HttpServletResponse.SC_FORBIDDEN);
            log.warn("User '" + request.getRemoteUser() + "' is trying to edit user '" +
                    request.getParameter("id") + "'");
            return null;
        }
        
        // if a user's id is passed in
        if (id != null) {
            // lookup the user using that id
        	user = UserMapper.mapModelToDto(userManager.getUser(id));
        	
        } else if (editProfile) {
        	user = UserMapper.mapModelToDto(userManager.getUserByUsername(request.getRemoteUser()));
        } else {
        	user = new UserDto();
        	user.addRole(Constants.USER_ROLE.toString());
        }
        if(user.getCentro() != null)
        currentcentro = user.getCentro().getId();
        if(user.getArea() != null)
    	currentarea = user.getArea().getId();

        if (user.getUsername() != null) {
            user.setConfirmPassword(user.getPassword());

            // if user logged in with remember me, display a warning that they can't change passwords
            log.debug("checking for remember me login...");

            AuthenticationTrustResolver resolver = new AuthenticationTrustResolverImpl();
            SecurityContext ctx = SecurityContextHolder.getContext();

            if (ctx != null) {
                Authentication auth = ctx.getAuthentication();

                if (resolver.isRememberMe(auth)) {
                    getSession().setAttribute("cookieLogin", "true");
                    saveMessage(getText(TextMessagesType.USERACTION_USERPROFILE_COOKIE_LOGIN.getKey()));
                }
            }
        }
        
        Set<Area> areas = areaManager.getAreas();
        for (Area area : areas) {
			comboareas.put(area.getId(), area.getNombre());
		}
        List<Centro> centros = centroManager.getCentros();
        for (Centro centro : centros) {
			combocentros.put(centro.getId(), centro.getNombre());
		}

        return SUCCESS;
    }

    /**
     * Default: just returns "success"
     *
     * @return "success"
     */
    public String execute() {
        return SUCCESS;
    }

    /**
     * Sends users to "enviosCentro" when !from.equals("list"). Sends everyone else to "cancel"
     *
     * @return "enviosCentro" or "cancel"
     */
    public String cancel() {
        if (!"list".equals(from)) {
            return "enviosCentro";
        }
        return "cancel";
    }

    /**
     * Save user
     *
     * @return success if everything worked, otherwise input
     * @throws Exception when setting "access denied" fails on response
     */
    public String save() throws Exception {

        /**
         * Validation....
         */
        if (user.getLoginType().equals(LoginType.INTERNAL)){
        	if(user.getPassword().trim().length()==0) {
        		return showUserPasswordError();
        	}
        }
        
        User hbUser = null;
        boolean isNew = false;
        if (user.getId() == null) {
        	hbUser = new User();
        	isNew = true;
        } else {
        	hbUser = userManager.get(user.getId());
        }
        
        //Transformar el UserDto en un User
        hbUser = UserMapper.mapDtoToModel(user, hbUser, null);
        
        // only attempt to change roles if user is admin
        // for other users, prepare() method will handle populating
        if (getRequest().isUserInRole(Constants.ADMIN_ROLE)) {
        	hbUser.getRoles().clear(); // APF-788: Removing roles from user doesn't work
            String[] userRoles = getRequest().getParameterValues("userRoles");

            for (int i = 0; userRoles != null && i < userRoles.length; i++) {
                String roleName = userRoles[i];
                try {
                	hbUser.addRole(roleManager.getRole(roleName));
                } catch (DataIntegrityViolationException e) {
                    return showUserExistsException();
                }
            }
        }
        
        Area a = null;
        if(idarea != null && !idarea.equals(""))
        	a = areaManager.buscarArea(idarea);
        Centro c = null;
        if(idcentro != null && !idcentro.equals(""))
        	c = centroManager.buscarCentro(idcentro);
        hbUser.setCentro(c);
        hbUser.setArea(a);
        try {
            userManager.saveUser(hbUser, encryptPass);
        } catch (AccessDeniedException ade) {
            // thrown by UserSecurityAdvice configured in aop:advisor userManagerSecurity
            log.warn(ade.getMessage());
            getResponse().sendError(HttpServletResponse.SC_FORBIDDEN);
            return null;
        } catch (UserExistsException e) {
            return showUserExistsException();
        }
        
        if(isNew) {
        	super.log(AuditType.INFO, "New user: " + hbUser.getUsername() + " <" + hbUser.getEmail() + ">");
        } else {
        	super.log(AuditType.INFO, "Edited user: " + hbUser.getUsername() + " <" + hbUser.getEmail() + ">");
        }
        
        HttpServletRequest request = getRequest();
        from = request.getParameter("from");
        if (from == null) {
        	from = "";
        }
        if (!(from.contains("list"))) {
            // add success messages
            addActionMessage(getText(TextMessagesType.USERACTION_USER_SAVED.getKey()));
            return "enviosCentro";
        } else {
            // add success messages
            List<Object> args = new ArrayList<Object>();
            args.add(user.getFullName());
            if (isNew) {
            	addActionMessage(getText(TextMessagesType.USERACTION_USER_ADDED.getKey(), args));
                // Send an account information e-mail
//                mailMessage.setSubject(getText(TextMessagesType.SIGNUP_EMAIL_SUBJECT.getKey()));
//                try {
//                    sendUserMessage(hbUser, getText(TextMessagesType.SIGNUP_EMAIL_MESSAGE.getKey(), args), RequestUtil.getAppURL(getRequest()));
//                } catch (MailException me) {
//                    addActionError(me.getLocalizedMessage());
//                }
                if( (request.getParameter("from") != null) && ((request.getParameter("from").contains("listldap")))) {
                	return SUCCESS_LDAP;
                }
                return SUCCESS;
            } else {
                user.setConfirmPassword(user.getPassword());
                addActionMessage(getText(TextMessagesType.USERACTION_USER_UPDATE_BY_ADMIN.getKey(), args));
                if( (request.getParameter("from") != null) && ((request.getParameter("from").contains("listldap")))) {
                	return SUCCESS_LDAP;
                }
                return SUCCESS;
            }
        }
    }
    
    private String showUserPasswordError() {
    	List<Object> args = new ArrayList<Object>();
        args.add(user.getUsername());
        args.add(user.getEmail());
        saveErrorMessage(getText(TextMessagesType.USERACTION_ERROR_EMPTY_PASSWORD.getKey(), args));

        // redisplay the unencrypted passwords
        return INPUT;
    	
    }

    private String showUserExistsException() {
        List<Object> args = new ArrayList<Object>();
        args.add(user.getUsername());
        args.add(user.getEmail());
        saveErrorMessage(getText(TextMessagesType.USERACTION_ERROR_EXISTING_USER.getKey(), args));

        // redisplay the unencrypted passwords
        user.setPassword(user.getConfirmPassword());
        return INPUT;
    }

    /**
     * Fetch all users from database and put into local "users" variable for retrieval in the UI.
     *
     * @return "success" if no exceptions thrown
     */
    public String list() {
        try {
            users = UserMapper.mapModelListToDtoList(userManager.search(query));
        } catch (SearchException se) {
        	saveErrorMessage(se.getMessage());
            users = UserMapper.mapModelListToDtoList(userManager.getUsers());
        }
        return SUCCESS;
    }

	public void setEncryptPass(Boolean encryptPass) {
		this.encryptPass = encryptPass;
	}

	public String thumbnailPhoto() {
		try {
			if (username != null && username != "") {
				//Check GRAVATAR
				User u = userManager.getUserByUsername(username);
				if (u.getEmail() != null) {
					String url = "http://www.gravatar.com/avatar/" + Md5Utils.md5(u.getEmail()) + "?s=130&d=404";
					URL gravatar = new URL(url);
			        imageView = gravatar.openStream();
			        return SUCCESS;
				}
				
				throw new Exception("No Thumbnail");
			}
		} catch (Exception e) {
			// return ERROR;
			try {
				String path = getSession().getServletContext().getRealPath("WEB-INF");
				setImageView(new FileInputStream(new File(path + "/../images/unknown-person.jpg")));
			} catch (Exception e1) {
				return ERROR;
			}
		}

		return SUCCESS;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public InputStream getImageView() {
		return imageView;
	}

	public void setImageView(InputStream imageView) {
		this.imageView = imageView;
	}

	public Long getIdarea() {
		return idarea;
	}

	public void setIdarea(Long idarea) {
		this.idarea = idarea;
	}

	public Long getIdcentro() {
		return idcentro;
	}

	public void setIdcentro(Long idcentro) {
		this.idcentro = idcentro;
	}

	public Map<Long, String> getComboareas() {
		return comboareas;
	}

	public void setComboareas(Map<Long, String> comboareas) {
		this.comboareas = comboareas;
	}

	public Map<Long, String> getCombocentros() {
		return combocentros;
	}

	public void setCombocentros(Map<Long, String> combocentros) {
		this.combocentros = combocentros;
	}

	public Long getCurrentcentro() {
		return currentcentro;
	}

	public void setCurrentcentro(Long currentcentro) {
		this.currentcentro = currentcentro;
	}

	public Long getCurrentarea() {
		return currentarea;
	}

	public void setCurrentarea(Long currentarea) {
		this.currentarea = currentarea;
	}
	
	
    
}
